#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <string.h>
#include "debug.h"
#include "fb_drv.h"


static void dump_fourcc(unsigned int fourcc)
{
    printf(" %c%c%c%c",
        fourcc,
        fourcc >> 8,
        fourcc >> 16,
        fourcc >> 24);
}

int fb_card_init(struct fb_card *card,char *devname){
    int fp=0;
    long mapsize=0;
    char *fbp = NULL;

    setbuf(stdout,NULL);

    fp = open(devname, O_RDWR);
    if(fp < 0) {
        printf("Error : Can not open framebuffer device/n");
        return -1;
    }
    card->fd=fp;

    if(ioctl(fp, FBIOGET_FSCREENINFO, &card->finfo)){
        printf("Error reading fixed information/n");
        return -1;
    }

    if(ioctl(fp, FBIOGET_VSCREENINFO, &card->vinfo)){
        printf("Error reading variable information/n");
        return -1;
    }

    mapsize = card->vinfo.xres_virtual * card->vinfo.yres_virtual * card->vinfo.bits_per_pixel / 8;

    fbp =(char *)mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fp,0);
    if ((int)fbp == -1)
    {
        printf ("Error: failed to map framebuffer device to memory./n");
        return -1;
    }
    printf("Get virt mem = %p\n", fbp);

    card->vaddr=fbp;
    card->screen_size=card->vinfo.xres * card->vinfo.yres * card->vinfo.bits_per_pixel / 8;
    card->buffer_count=(card->vinfo.xres_virtual * card->vinfo.yres_virtual)/(card->vinfo.xres * card->vinfo.yres);

    /* using first frame, for FBIOPAN_DISPLAY
     * 当刷新需要调用FBIOPAN_DISPLAY， 要告知驱动刷哪块帧， 用到下面两个参数
     * 如果使用第二帧buffer -> vinfo.xoffset = 0; vinfo.yoffset = vinfo.yres;
     */
    card->buffer_index=0;
    card->vinfo.xoffset = 0;
    card->vinfo.yoffset = card->vinfo.yres*card->buffer_index;
    ioctl(fp, FBIOPAN_DISPLAY, &card->vinfo);

    return 0;
}


int fb_card_print_info (struct fb_card card){
    int         fd_fb0;
    int         rtn;
    struct fb_fix_screeninfo fix;
    struct fb_var_screeninfo var;

    if(card.fd==0){
       pr_db("erro");
    }
    fix=card.finfo;
    var=card.vinfo;

    if(var.bits_per_pixel/8==2){
        printf("framebuffer format  : rgb565 \r\n");
    }
    else if (var.bits_per_pixel/8==3){
        printf("framebuffer format  : rgb888 \r\n");
    }
    else if (var.bits_per_pixel/8==4){
        printf("framebuffer format  : argb8888 \r\n");
    }
    printf("screen width = %u,height = %u\n\r", var.xres, var.yres);

    printf("framebuffer number : %d \r\n",(var.xres_virtual*var.yres_virtual)/(var.xres*var.yres));

    return 0;

    //
    printf("======== var screeninfo ========\n\r");
    printf("xres = %u\n\r", var.xres);
    printf("yres = %u\n\r", var.yres);
    printf("xres_virtual = %u\n\r", var.xres_virtual);
    printf("yres_virtual = %u\n\r", var.yres_virtual);
    printf("xoffset = %u\n\r", var.xoffset);
    printf("yoffset = %u\n\r\n\r", var.yoffset);

    printf("bits_per_pixel = %u\n\r", var.bits_per_pixel);
    printf("grayscale = %u\n\r\n\r", var.grayscale);

    printf("fb_bitfield red, offset = %u, length = %u, right = %u, \n\r", var.red.offset, var.red.length, var.red.msb_right);
    printf("fb_bitfield green, offset = %u, length = %u, right = %u, \n\r", var.green.offset, var.green.length, var.green.msb_right);
    printf("fb_bitfield blue, offset = %u, length = %u, right = %u, \n\r", var.blue.offset, var.blue.length, var.blue.msb_right);
    printf("fb_bitfield transp, offset = %u, length = %u, right = %u, \n\r\n\r", var.transp.offset, var.transp.length, var.transp.msb_right);

    printf("nonstd  = %u\n\r\n\r", var.nonstd);

    printf("activate  = %u\n\r\n\r", var.activate);

    printf("height = %u\n\r", var.height);
    printf("width = %u\n\r\n\r", var.width);

    printf("accel_flags = %u\n\r\n\r", var.accel_flags);

    printf("pixclock = %u\n\r", var.pixclock);
    printf("left_margin = %u\n\r", var.left_margin);
    printf("right_margin = %u\n\r", var.right_margin);
    printf("upper_margin = %u\n\r", var.upper_margin);
    printf("lower_margin = %u\n\r", var.lower_margin);
    printf("hsync_len = %u\n\r", var.hsync_len);
    printf("vsync_len = %u\n\r", var.vsync_len);
    printf("sync = %u\n\r", var.sync);
    printf("vmode = %u\n\r", var.vmode);
    printf("rotate = %u\n\r", var.rotate);
    printf("colorspace = %u\n\r\n\r", var.colorspace);
//    dump_fourcc(var.colorspace);


    //
    printf("======== fix screeninfo ========\n\r");
    printf("id = %s\n\r", fix.id);
    printf("smem_len = %u\n\r", fix.smem_len);
    printf("type = %u\n\r", fix.type);
    printf("type_aux = %u\n\r", fix.type_aux);
    printf("visual = %u\n\r\n\r", fix.visual);

    printf("xpanstep = %u\n\r", fix.xpanstep);
    printf("ypanstep = %u\n\r", fix.ypanstep);
    printf("ywrapstep = %u\n\r\n\r", fix.ywrapstep);

    printf("line_length = %u\n\r", fix.line_length);
    printf("mmio_len = %u\n\r", fix.mmio_len);


    return 0;
}

int fb_switch_buffer(struct fb_card *card,unsigned int index){
    if(index>=card->buffer_count)
        index=card->buffer_count-1;
    card->buffer_index=index;
      card->vinfo.xoffset = 0;
      card->vinfo.yoffset = card->vinfo.yres*card->buffer_index;
      ioctl(card->fd, FBIOPAN_DISPLAY, &card->vinfo);
    return 0;
}

int fb_cpy(struct fb_card *card,void * addr,unsigned int size){
    if(card->vaddr!=NULL){
        if(size<=card->screen_size){
              memcpy(card->vaddr+(card->buffer_index*card->screen_size),addr,size);
        }
        else{
            fprintf(stderr,"buffer size  too long\r\n");
        }
    }
    else{
        fprintf(stderr,"plane not init\r\n");
    }
}

int fb_double_cpy(struct fb_card *card,void * addr,unsigned int size){
//    printf("buffer index: %d card->buffer_count: %d\r\n",card->buffer_index,card->buffer_count);
    card->buffer_index=(card->buffer_index+1)%(card->buffer_count);
    if(card->vaddr!=NULL){
        if(size<=card->screen_size){
              memcpy(card->vaddr+(card->buffer_index*card->screen_size),addr,size);
              card->vinfo.xoffset = 0;
              card->vinfo.yoffset = card->vinfo.yres*card->buffer_index;
              ioctl(card->fd, FBIOPAN_DISPLAY, &card->vinfo);
//              printf("buffer index: %d\r\n",card->buffer_index);
        }
        else{
            fprintf(stderr,"buffer size  too long\r\n");
        }
    }
    else{
        fprintf(stderr,"plane not init\r\n");
    }
}
